#include "TestMemoryManager.h"

TestMemoryManager::TestMemoryManager() {
	manager = new Manager();
}

TestMemoryManager::~TestMemoryManager() {
	delete manager;
}

void TestMemoryManager::TestAll() {
	TestCreateAndDestroy();
	TestHandles(1000);
}

void TestMemoryManager::TestCreateAndDestroy() {

	Manager& m = Manager::getInstance();
	bufferId_t pool = m.createPoolBuffer(sizeof(SmallData), 1000);
	bufferId_t stack = m.createStackBuffer(sizeof(SmallData), 1000);
	
	SizeVector elementSizes, capacities;
	elementSizes.push_back(sizeof(SmallData));  capacities.push_back(500);
	elementSizes.push_back(sizeof(MediumData)); capacities.push_back(200);
	elementSizes.push_back(sizeof(LargeData));  capacities.push_back(100);

	bufferId_t managed = m.createManagedPoolBuffer(elementSizes, capacities);

	Manager::Handle poolHandle, stackHandle, managedHandle;
	SmallData* poolData		= m.acquire<SmallData>(pool, poolHandle);
	SmallData* stackData	= m.acquire<SmallData>(stack, stackHandle);
	SmallData* managedData	= m.acquire<SmallData>(managed, managedHandle);

	SmallData testData;
	testData.random();

	*poolData = testData;
	*stackData = testData;
	*managedData = testData;

	assert( testData == *(m.dereference<SmallData>(poolHandle)) );
	assert( testData == *(m.dereference<SmallData>(stackHandle)) );
	assert( testData == *(m.dereference<SmallData>(managedHandle)) );

	m.release(poolHandle);
	m.release(stackHandle);
	m.release(managedHandle);

	m.releaseBuffer(pool);
	m.releaseBuffer(stack);
	m.releaseBuffer(managed);

}

void TestMemoryManager::TestHandles(size_t capacity) {

	Manager& m = Manager::getInstance();

	SizeVector elementSizes, capacities;
	elementSizes.push_back(sizeof(SmallData));  capacities.push_back(capacity/3);
	elementSizes.push_back(sizeof(MediumData)); capacities.push_back(capacity/3);
	elementSizes.push_back(sizeof(LargeData));  capacities.push_back(capacity/3);
	capacity = (capacity/3)*3;

	bufferId_t managed = m.createManagedPoolBuffer(elementSizes, capacities);

	std::vector< Manager::Handle > handles;
	std::vector< unsigned int > types;
	std::vector< bool > usedIndices(capacity,true);

	SmallData smallData;
	MediumData mediumData;
	LargeData largeData;
	
	smallData.random();
	mediumData.random();
	largeData.random();

	// acquire and assign data

	size_t dataSize = 0;
	for(unsigned int i=0; i < capacity; ++i) {
		Manager::Handle handle;
		
		types.push_back(i%3);

		switch(types.back()) {
		case 0: 
			{
				SmallData* data = m.acquire<SmallData>(managed, handle);
				*data = smallData;
			}
			break;
		case 1:
			{
				MediumData* data = m.acquire<MediumData>(managed, handle);
				*data = mediumData;
			}
			break;
		case 2:
			{
				LargeData* data = m.acquire<LargeData>(managed, handle);
				*data = largeData;
			}
			break;
		}

		handles.push_back(handle);
	}

	// release in random order and check if that destroys non-released data
	IndexVector* randomIndices = shuffle(handles.size());
	
	for(unsigned int j=0; j < randomIndices->size(); ++j) {

		// check remaining data
		for(unsigned int i=0; i<handles.size(); ++i) {
			if(usedIndices[i] == false) continue;

			switch(types[i]) {
			case 0: 
				{
					SmallData* data = m.dereference<SmallData>(handles[i]);
					assert(*data == smallData && "Assigned and retrieved data dont match");
				}
				break;
			case 1:
				{
					MediumData* data = m.dereference<MediumData>(handles[i]);
					assert(*data == mediumData && "Assigned and retrieved data dont match");
				}
				break;
			case 2:
				{
					LargeData* data = m.dereference<LargeData>(handles[i]);
					assert(*data == largeData && "Assigned and retrieved data dont match");
				}
				break;

			}
		}

		// release random
		unsigned int ri = randomIndices->at(j);
		m.release(handles[ri]);
		usedIndices[ri] = false;
	}
	
	delete randomIndices;

	m.releaseBuffer(managed);

}
